﻿using DocumentFormat.OpenXml.Lite.Core.ContentType;
using DocumentFormat.OpenXml.Lite.Core.Office;
using DocumentFormat.OpenXml.Lite.Core.Relationship;
using DocumentFormat.OpenXml.Lite.WordprocessingML.Word;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DFS = DocumentFormat.OpenXml.Lite.Core.DocumentFS;
using System.Reflection;
using DocumentFormat.OpenXml.Lite.WordprocessingML.Word.Base;
using DocumentFormat.OpenXml.Lite.WordprocessingML.Word.Style;
using DocumentFormat.OpenXml.Lite.Shared;
using DocumentFormat.OpenXml.Lite.Helper;
using DocumentFormat.OpenXml.Lite.Core.Interfaces;

namespace DocumentFormat.OpenXml.Lite.WordprocessingML
{
    internal class WordDocument : DocumentBase
    {
        private int charts = 0, images = 0;
        public Document Document { get; private set; }

        public WordDocument(Guid id, DocumentType type)
            : base(id, type)
        {
        }

        public override bool ExtractFile(string file, System.IO.Stream content)
        {
            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            if (file == @"[Content_Types].xml")
            {
                ContentTypeManager.Deserialize(content);
                return true;
            }
            else if (file == @"_rels\.rels" || file == @"_rels/.rels")
            {
                var relManager = sandbox.GetDirectory("_rels").GetFile(".rels").Object as RelationshipManager;
                relManager.Deserialize(content);
                return true;
            }
            else if (file == @"word\document.xml" || file == @"word/document.xml")
            {
                Document.Deserialize(content);
                return true;
            }
            else if (file == @"word\footer.xml" || file == @"word/footer.xml")
            {
                Footer f = new Footer();
                f.Deserialize(content);
                f.DocumentId = Id;

                sandbox.CreateFile(file.Replace('\\', '/'), f);
                return true;
            }
            else if (file == @"word\header.xml" || file == @"word/header.xml")
            {
                Header h = new Header();
                h.Deserialize(content);
                h.DocumentId = Id;

                sandbox.CreateFile(file.Replace('\\', '/'), h);
                return true;
            }
            else if (file.StartsWith(@"word\styles.xml") || file.StartsWith(@"word/styles.xml"))
            {
                Styles s = new Styles();
                s.Deserialize(content);
                s.DocumentId = Id;

                sandbox.CreateFile(file.Replace('\\', '/'), s);
                return true;
            }
            else if (file.StartsWith(@"word\embeddings\") || file.StartsWith(@"word/embeddings/"))
            {
                Embedding em = new Embedding(null, file);
                em.Deserialize(content);

                sandbox.CreateFile(file.Replace('\\', '/'), em);
            }
            else if (file.StartsWith(@"word\charts\chart") || file.StartsWith(@"word/charts/chart"))
            {
                charts++;
                System.Type t = System.Type.GetType("DocumentFormat.OpenXml.Lite.DrawingML.Helper, DocumentFormat.OpenXml.Lite.DrawingML");
                if (t != null)
                {
                    MethodInfo method = t.GetMethod("ExtractChart", BindingFlags.Static | BindingFlags.Public);
                    method.Invoke(null, new object[] { file, content, sandbox });

                    return true;
                }
            }
            else if (file.StartsWith(@"word\theme\theme") || file.StartsWith(@"word/theme/theme"))
            {
                System.Type t = System.Type.GetType("DocumentFormat.OpenXml.Lite.DrawingML.Helper, DocumentFormat.OpenXml.Lite.DrawingML");
                if (t != null)
                {
                    MethodInfo method = t.GetMethod("ExtractTheme", BindingFlags.Static | BindingFlags.Public);
                    method.Invoke(null, new object[] { file, content, sandbox });

                    return true;
                }
            }
            else if (file.StartsWith(@"word\media\") || file.StartsWith(@"word/media/"))
            {
                ImageType type = ImageType.None;
                string extension = file.Split('.')[1];
                switch (extension)
                {
                    case "bmp":
                        type = ImageType.BMP;
                        break;
                    case "gif":
                        type = ImageType.GIF;
                        break;
                    case "jpeg":
                        type = ImageType.JPEG;
                        break;
                    case "jpg":
                        type = ImageType.JPG;
                        break;
                    case "png":
                        type = ImageType.PNG;
                        break;
                    case "tiff":
                        type = ImageType.TIFF;
                        break;
                    default: break;
                }

                if (type == ImageType.None)
                {
                    return false;
                }

                images++;
                Image img = new Image(null, type);
                img.Deserialize(content);

                sandbox.CreateFile(file.Replace('\\', '/'), img);

                return true;
            }
            else if (file.EndsWith(@".rels"))
            {
                var relManager = new RelationshipManager();
                relManager.Deserialize(content);
                sandbox.CreateFile(file.Replace('\\', '/'), relManager);
                return true;
            }

            return base.ExtractFile(file, content);
        }

        public override void CreateDefault()
        {
            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);

            DFS.Directory relDir = sandbox.CreateDirectory("_rels");
            DFS.Directory wordDir = sandbox.CreateDirectory("word");

            Document = new Document(this);
            DFS.File documentXml = wordDir.CreateFile("document.xml", Document);

            RelationshipManager relManager = new RelationshipManager();
            DFS.File relFile = relDir.CreateFile(".rels", relManager);
            relManager.CreateRelationship(RelationshipURI.OfficeDocument, documentXml);

            RelationshipManagers.Add(relManager);

            ContentTypeManager = new ContentTypeManager();
            DFS.File contFile = sandbox.CreateFile("[Content_Types].xml", ContentTypeManager);

            ContentTypeManager.CreateDefault(ContentType.Xml, ExtensionType.Xml);
            ContentTypeManager.CreateDefault(ContentType.Rels, ExtensionType.Rels);
            ContentTypeManager.CreateOverride(ContentType.OfficeWord, documentXml);
        }

        public override string AddEmbeddingFor(ISerializable obj, Embedding em, RelationshipURI relUri, Default def)
        {
            this.ContentTypeManager.Defaults.Add(def);

            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            if (!sandbox.ExistsDirectory("word/embeddings"))
            {
                sandbox.CreateDirectory("word/embeddings");
            }

            DFS.File ef = sandbox.CreateFile("word/embeddings/" + em.FileName, em);
            DFS.File target = sandbox.GetDirectory("word").GetFilesRecursive().FirstOrDefault(f => f.Object == obj);
            string relFileName = target.Name;
            if (!target.Parent.ExistsDirectory("_rels"))
            {
                CreateRelationsipFile(target.Parent, relFileName + ".rels");
            }
            else if (!target.Parent.ExistsFile(relFileName + ".rels"))
            {
                RelationshipManager rm = new RelationshipManager();
                target.Parent.CreateFile(relFileName + ".rels", rm);
            }

            DFS.File relFile = target.Parent.GetFile("_rels/" + relFileName + ".rels");
            RelationshipManager relManager = relFile.Object as RelationshipManager;
            int id = relManager.CreateRelationship(relUri, ef, target.Parent);

            return RelationshipManager.GetRelationshipId(id);
        }

        public Styles CreateStyle()
        {
            Styles styles = new Styles();
            styles.DocumentId = this.Id;

            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            DFS.File styleFile = sandbox.CreateFile("word/styles.xml", styles);
            this.ContentTypeManager.CreateOverride(ContentType.WordStyle, styleFile);

            if (!sandbox.ExistsDirectory("word/_rels"))
            {
                CreateRelationsipFile(sandbox.GetDirectory("word"), "document.xml.rels");
            }

            DFS.File relFile = sandbox.GetFile("word/_rels/document.xml.rels");
            RelationshipManager relManager = relFile.Object as RelationshipManager;
            relManager.CreateRelationship(RelationshipURI.Style, styleFile, sandbox.GetDirectory("word"));

            return styles;
        }

        public Header CreateHeader()
        {
            Header hdr = new Header();
            hdr.DocumentId = this.Id;

            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            DFS.File hdrFile = sandbox.CreateFile("word/header.xml", hdr);
            this.ContentTypeManager.CreateOverride(ContentType.WordHeader, hdrFile);

            if (!sandbox.ExistsDirectory("word/_rels"))
            {
                CreateRelationsipFile(sandbox.GetDirectory("word"), "document.xml.rels");
            }

            DFS.File relFile = sandbox.GetFile("word/_rels/document.xml.rels");
            RelationshipManager relManager = relFile.Object as RelationshipManager;
            int rid = relManager.CreateRelationship(RelationshipURI.WordHeader, hdrFile, sandbox.GetDirectory("word"));
            hdr.RelationshipId = RelationshipManager.GetRelationshipId(rid);

            return hdr;
        }

        public Footer CreateFooter()
        {
            Footer ftr = new Footer();
            ftr.DocumentId = this.Id;

            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            DFS.File ftrFile = sandbox.CreateFile("word/footer.xml", ftr);
            this.ContentTypeManager.CreateOverride(ContentType.WordFooter, ftrFile);

            if (!sandbox.ExistsDirectory("word/_rels"))
            {
                CreateRelationsipFile(sandbox.GetDirectory("word"), "document.xml.rels");
            }

            DFS.File relFile = sandbox.GetFile("word/_rels/document.xml.rels");
            RelationshipManager relManager = relFile.Object as RelationshipManager;
            int rid = relManager.CreateRelationship(RelationshipURI.WordFooter, ftrFile, sandbox.GetDirectory("word"));
            ftr.RelationshipId = RelationshipManager.GetRelationshipId(rid);

            return ftr;
        }
        
        public string CreateChartElement(IDrawingElement chart)
        {
            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            if (!sandbox.ExistsDirectory("word/charts"))
            {
                sandbox.CreateDirectory("word/charts");
            }
            DFS.File chartFile = sandbox.CreateFile("word/charts/chart" + (++charts) + ".xml", chart);
            this.ContentTypeManager.CreateOverride(ContentType.Chart, chartFile);

            if (!sandbox.ExistsDirectory("word/_rels"))
            {
                CreateRelationsipFile(sandbox.GetDirectory("word"), "document.xml.rels");
            }

            DFS.File relFile = sandbox.GetFile("word/_rels/document.xml.rels");
            RelationshipManager relManager = relFile.Object as RelationshipManager;
            int id = relManager.CreateRelationship(RelationshipURI.Chart, chartFile, sandbox.GetDirectory("word"));
            return RelationshipManager.GetRelationshipId(id);
        }

        public string CreatePictureElementFor(ISerializable obj, Image image)
        {
            this.ContentTypeManager.CreateDefaultForImage(image);

            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(Id);
            if (!sandbox.ExistsDirectory("word/media"))
            {
                sandbox.CreateDirectory("word/media");
            }
            DFS.File picFile = sandbox.CreateFile("word/media/image" + 
                (++images) + "." + image.Type.GetDescription(), image);

            string relFileName = sandbox.GetDirectory("word").GetFiles().FirstOrDefault(f => f.Object == obj).Name;
            if (!sandbox.ExistsDirectory("word/_rels"))
            {
                CreateRelationsipFile(sandbox.GetDirectory("word"), relFileName + ".rels");
            }
            else if (!sandbox.ExistsFile("word/_rels/" + relFileName + ".rels"))
            {
                RelationshipManager rm = new RelationshipManager();
                sandbox.CreateFile("word/_rels/" + relFileName + ".rels", rm);
            }

            DFS.File relFile = sandbox.GetFile("word/_rels/" + relFileName + ".rels");
            RelationshipManager relManager = relFile.Object as RelationshipManager;
            int id = relManager.CreateRelationship(RelationshipURI.Image, picFile, sandbox.GetDirectory("word"));

            return RelationshipManager.GetRelationshipId(id);
        }

        private void CreateRelationsipFile(DFS.Directory dir, string fileName)
        {
            RelationshipManager relManager = new RelationshipManager();
            RelationshipManagers.Add(relManager);

            DFS.Directory relDir = dir.CreateDirectory("_rels");
            DFS.File relFile = relDir.CreateFile(fileName, relManager);
        }
    }
}
